#ifndef TIEC_FILE_H
#define TIEC_FILE_H

#include <fstream>
#include <string>
#include <vector>
#include <cstdint>
#include <algorithm>
#include <list>

#include "macro.h"
#include "data.h"

namespace NS_TIEC {
  /// 文件实现
  class File {
  public:
    static char separator;

    File(const File& file) = default;
    File(File&& file) noexcept;
    explicit File(String&& path);
    explicit File(const String& path);
    explicit File(const String& dir, const String& filename);
    File& operator=(const File& other) = default;
    File& operator=(File&& other) = default;
    bool operator==(const File& other) const;

    /// 判断文件是否存在
    /// \return 文件存在则返回true
    bool exists() const;

    /// 判断文件是否为文件不是文件夹
    bool isFile() const;

    /// 判断文件是否为文件夹不是文件
    bool isDirectory() const;

    /// 取文件大小
    /// \return 文件字节长度
    UInt64 size() const;

    /// 取文件名
    /// \return 完整的文件名
    String getName() const;

    /// 取文件扩展名
    /// \return 扩展名
    String getExtension() const;

    /// 获取文件最后一次更新的毫秒时间戳
    /// \return 更新时间
    UInt64 lastUpdateTime() const;

    /// 创建目录
    /// \return 只能创建单级目录，不支持多级
    bool mkdir() const;

    /// 创建目录
    /// \return 支持多级目录
    bool mkdirs() const;

    /// 取父目录
    /// \return 父目录文件
    File getParentFile() const;

    /// 获取目录下所有文件
    std::list<File> listFiles() const;

    /// 获取目录下所有文件路径
    std::list<String> listPaths() const;

    /// 通过相对路径获得文件
    /// \param relative_path 相对路径
    File relativeFile(const String& relative_path) const;

    /// 获取规范化的绝对路径（会把../替换为实际目录）构造的File对象
    /// @return 规范化后的File
    File getCanonicalFile() const;

    /// 获取文件路径宽字符
    /// \return 如果文件传入的是相对路径，那返回的是相对路径
    String& getPath();

    /// 获取文件路径
    /// \return 如果文件传入的是相对路径，那返回的是相对路径
    String getPath() const;

    /// 获取规范化的绝对路径（会把../替换为实际目录）
    /// @return 规范化的绝对路径
    String getCanonicalPath() const;

    /// 获取文件绝对路径
    /// \return 绝对路径
    String getAbsolutePath() const;

    /// 重命名文件
    /// \param new_file 新的文件
    /// \return 重命名成功返回true
    bool renameTo(const File& new_file) const;

    /// 读取文件内容为字符串
    /// \return 文件字符串内容
    String readString() const;

    /// 读取文件内容为字节集
    /// \return 文件字节
    ByteArray readBytes() const;

    /// 向文件写入内容
    /// \param text 字符串内容
    bool writeString(const String& text) const;

    /// 向文件写入字节集
    /// \param bytes 字节集内容
    bool writeBytes(const ByteArray& bytes) const;

    /// 转换为Uri
    Uri toUri() const;
  private:
    String path_;
  };

  /// 二进制文件实现
  class BinaryFile {
  public:
    BinaryFile() = default;
    ~BinaryFile();
    
    /// 打开文件
    /// @param filename 文件路径
    bool open(const String& filename, std::ios::openmode mode);
    
    /// 关闭文件
    void close();
    
    /// 设置字节序（默认小端序）
    /// @param is_big_endian 是否使用大端序
    void setBigEndian(bool is_big_endian);
    
    /// 检查文件是否打开
    bool isOpen() const;

    void writeInt64(int64_t value);
    void writeUInt64(uint64_t value);
    void writeInt32(int32_t value);
    void writeUInt32(uint32_t value);
    void writeInt16(int16_t value);
    void writeUInt16(uint16_t value);
    void writeChar(char value);
    void writeByte(uint8_t value);
    void writeFloat(float value);
    void writeDouble(double value);
    void writeString(const String& str);
    void writeBytes(const void* data, size_t size);

    int64_t readInt64();
    uint64_t readUInt64();
    int32_t readInt32();
    uint32_t readUInt32();
    int16_t readInt16();
    uint16_t readUInt16();
    char readChar();
    uint8_t readByte();
    float readFloat();
    double readDouble();
    String readString();
    void readBytes(void* data, size_t size);

    void seek(size_t position);
    size_t tell();
    bool good() const;
    bool eof() const;
  private:
    std::fstream file_;
    bool big_endian_ {false};

    /// 判断当前系统是否为小端序
    inline static bool isLittleEndian() {
      static const uint16_t test = 0x0001;
      return (*reinterpret_cast<const uint8_t*>(&test) == 0x01);
    }

    /// 16位整数字节序转换
    inline static uint16_t swap16(uint16_t value) {
      return (value << 8) | (value >> 8);
    }

    /// 32位整数字节序转换
    inline static uint32_t swap32(uint32_t value) {
      return (value << 24) |
           ((value << 8) & 0x00FF0000) |
           ((value >> 8) & 0x0000FF00) |
           (value >> 24);
    }

    /// 64位整数字节序转换
    inline static uint64_t swap64(uint64_t value) {
      return (value << 56) |
           ((value << 40) & 0x00FF000000000000ULL) |
           ((value << 24) & 0x0000FF0000000000ULL) |
           ((value << 8)  & 0x000000FF00000000ULL) |
           ((value >> 8)  & 0x00000000FF000000ULL) |
           ((value >> 24) & 0x0000000000FF0000ULL) |
           ((value >> 40) & 0x000000000000FF00ULL) |
           (value >> 56);
    }

#ifndef htons
    /// 主机字节序转网络字节序 (16位)
    inline static uint16_t htons(uint16_t host) {
      return isLittleEndian() ? swap16(host) : host;
    }
#endif

#ifndef htonl
    /// 主机字节序转网络字节序 (32位)
    inline static uint32_t htonl(uint32_t host) {
      return isLittleEndian() ? swap32(host) : host;
    }
#endif

#ifndef htonll
    /// 主机字节序转网络字节序 (64位)
    inline static uint64_t htonll(uint64_t host) {
      return isLittleEndian() ? swap64(host) : host;
    }
#endif

#ifndef ntohs
    /// 网络字节序转主机字节序 (16位)
    inline static uint16_t ntohs(uint16_t net) {
      return isLittleEndian() ? swap16(net) : net;
    }
#endif

#ifndef ntohl
    /// 网络字节序转主机字节序 (32位)
    inline static uint32_t ntohl(uint32_t net) {
      return isLittleEndian() ? swap32(net) : net;
    }
#endif

#ifndef ntohll
    /// 网络字节序转主机字节序 (64位)
    inline static uint64_t ntohll(uint64_t net) {
      return isLittleEndian() ? swap64(net) : net;
    }
#endif
  };

  struct FileHash {
    size_t operator()(const File& file) const;
  };

  struct FileEqual {
    bool operator()(const File& lhs, const File& rhs) const;
  };

  using FileSet = Set<File, FileHash, FileEqual>;
}

#endif //TIEC_FILE_H
